package com.powergisol.gis.service.impl;

import com.powergisol.core.analysis.SpatialAnalysis;
import com.powergisol.core.math.Dot;
import com.powergisol.core.math.Line;
import com.powergisol.core.math.Plane;
import com.powergisol.core.math.Vector;
import com.powergisol.core.model.*;
import com.powergisol.gis.dto.ExcavatingDTO;
import com.powergisol.gis.dto.ModuleDTO;
import com.powergisol.gis.dto.SectionDTO;
import com.powergisol.gis.geom.GeoLineString;
import com.powergisol.gis.geom.GeoPoint;
import com.powergisol.gis.geom.GeoPolygon;
import com.powergisol.gis.service.ExcavatingService;
import com.powergisol.gis.service.SectionService;
import com.powergisol.gis.support.ModuleResolver;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Polygon;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ExcavatingServiceImpl implements ExcavatingService {



    @Autowired
    SectionService sectionService;

    @Autowired
    ModuleResolver moduleResolver;
    private List<List<Double>> points;
    private List<List<Integer>> conbination;
    //private List<Tangent> tangentList;

    public static void main(String[] args) {
        ExcavatingServiceImpl excavatingService = new ExcavatingServiceImpl();

        Coordinate[] coordinates = new Coordinate[]{
                new Coordinate(104.08420695, 27.9557556, 10),
                new Coordinate(104.09408171, 27.95123462, 10),
                new Coordinate(104.1007442, 27.94814132, 10),
                new Coordinate(104.1373879, 27.94278754, 10),
                new Coordinate(104.14607293, 27.92874872, 10),
                new Coordinate(104.12620443, 27.91994471, 10),
                new Coordinate(104.11585378, 27.93493531, 10),
                new Coordinate(104.08337414, 27.93541121, 10),
        };

        GeoPolygon geoPolygon = new GeoPolygon(coordinates);

        List<GeoLineString> strings = excavatingService.getSectLineString(geoPolygon);

        Coordinate coordinateA = new Coordinate(104.12049373, 27.94980695, 5);
        Coordinate coordinateB = new Coordinate(104.12775108, 27.94052705, 5);
        Coordinate coordinateC = new Coordinate(104.11502097, 27.94243062, 5);

        excavatingService.points = new ArrayList<>();
        excavatingService.points.add(Arrays.asList(coordinateA.x, coordinateA.y, coordinateA.z));
        excavatingService.points.add(Arrays.asList(coordinateB.x, coordinateB.y, coordinateB.z));

        List<PointWithIndex> points = Arrays.asList(new PointWithIndex(coordinateA, 0), new PointWithIndex(coordinateB, 1), new PointWithIndex(coordinateC, 2));
        /**
         * 1个点在外 2个点在里面
         */
        TriangleMesh thisMesh = new TriangleMesh(points);

        /**
         * 2个点在外 1个点在里面
         */
        TriangleMesh thisMesh1 = new TriangleMesh(Arrays.asList(new PointWithIndex(new Coordinate(104.10478929, 27.95004489, 5), 0), new PointWithIndex(new Coordinate(104.11323637, 27.94861721, 5), 1), new PointWithIndex(new Coordinate(104.10633594, 27.94302548, 5), 2)));


        TriangleMesh thisMesh2 = new TriangleMesh(Arrays.asList(new PointWithIndex(new Coordinate(104.08420695, 27.9557556, 5), 0), new PointWithIndex(new Coordinate(104.076570166, 27.946875052, 5), 1), new PointWithIndex(new Coordinate(104.096343487, 27.956868788, 5), 2)));

        TriangleMesh thisMesh3 = new TriangleMesh(Arrays.asList(new PointWithIndex(new Coordinate(104.140719146, 27.941121912, 5), 0), new PointWithIndex(new Coordinate(104.135127413, 27.917327302, 5), 1), new PointWithIndex(new Coordinate(104.142741688, 27.929938445, 5), 2)));


        /**
         * 1个点与顶点相交,另外两个点在外
         */
        TriangleMesh thisMesh4 = new TriangleMesh(Arrays.asList(new PointWithIndex(new Coordinate(104.08420695, 27.9557556, 5), 0), new PointWithIndex(new Coordinate(104.086298 , 27.966730 , 5), 1), new PointWithIndex(new Coordinate(104.073033 , 27.961376, 5), 2)));

        /**
         * 1个点与顶点相交,另外两个点在内
         */
        TriangleMesh thisMesh5 = new TriangleMesh(Arrays.asList(new PointWithIndex(new Coordinate(104.12620443, 27.91994471, 5), 0), new PointWithIndex(new Coordinate(104.13224938 , 27.92648291 , 5), 1), new PointWithIndex(new Coordinate(104.12525377 , 27.92767264, 5), 2)));


        TriangleMesh thisMesh6 = new TriangleMesh(Arrays.asList(new PointWithIndex(new Coordinate(104.096343487, 27.956868788, 5), 0), new PointWithIndex(new Coordinate(104.08420695 , 27.955755597, 5), 1), new PointWithIndex(new Coordinate(104.08399708 , 27.950628726, 5), 2)));


        //Result result0 = excavatingService.justDoIt(thisMesh, strings, 0);
        //System.out.println(result0);

        //Result result1 = excavatingService.justDoIt(thisMesh1, strings, 0);
        //System.out.println(result1);


        //Result result2 = excavatingService.justDoIt(thisMesh2, strings, 0);
        //System.out.println(result2);

        //Result result3 = excavatingService.justDoIt(thisMesh3, strings, 0);
        //System.out.println(result3);

        //Result result4 = excavatingService.justDoIt(thisMesh4, strings, 0);
       // System.out.println(result4);


        //Result result5 = excavatingService.justDoIt(thisMesh5, strings, 0);
        //System.out.println(result5);

        Result result6 = excavatingService.justDoIt(thisMesh6, strings, 0);
        log.info(result6.toString());
    }

    @Override
    public ExcavatingDTO createExcavating(GeoPolygon geoPolygon, int height, String plane) throws IOException {
        //step 1 : 找到开挖多边形各个顶点

        Coordinate[] coordinates = geoPolygon.getCoordinates();

        //step 2 ： 判断各个顶点在哪些三角面里面，我们这里一层一层的处理
        List<ModuleDTO> listModuleDTO = moduleResolver.getListModuleDTO(plane);


        listModuleDTO.forEach(moduleDTO -> {
            //step3 : 从新生成三角形，删掉原有的三角形，新加入新的三角形
            //List<List<Integer>> conbination = createNewIndex(moduleDTO,coordinates);

        });


        return createNewIndex(geoPolygon, listModuleDTO.get(2));
    }


    private Set<List<Integer>> checkList(List<List<Integer>> copyList){


        Set<List<Integer>> listSet = new HashSet<>();

        for (List<Integer> list : copyList) {
            if(listSet.contains(list)){
                log.warn("存在相同的三角网序列"+list);
            }
            listSet.add(list);
        }

        return listSet;
    }

    private String createSplKey(double... velues){
        BigDecimal temp = new BigDecimal("0");
        for (double velue : velues) {
            BigDecimal bigDecimal = new BigDecimal(Double.toString(velue));
            temp = temp.add(bigDecimal);
        }
        return temp.toString();
    }

    public ExcavatingDTO createNewIndex(GeoPolygon geoPolygon, ModuleDTO moduleDTO) {

        //tangentList = new ArrayList<>();

        /**
         * 顶点集合
         */
        points = moduleDTO.getPoints();
        /**
         * 构成三角网的顶点的索引集合
         */
        conbination = moduleDTO.getConbination();

        int level = moduleDTO.getLevel();
        /**
         * 构成三角网的顶点的索引集合
         */
        List<List<Integer>> copyList = new CopyOnWriteArrayList(conbination);


        /**
         * 未切到的三角网
         */
        List<List<Integer>> include = new ArrayList<>();
        /**
         * 被包含的三角网
         */
        List<List<Integer>> outSide = new ArrayList<>();
        /**
         * 被切到的三角网
         */
        List<List<Integer>> intersects = new ArrayList<>();


        /**
         * 特别的相交点
         */
        Map<String,List<List<Integer>>> specialIntersects = new HashMap<>();

        /**
         * 将相交的数据集变为一个可变的数据集
         */
        List<List<Integer>> newIndexs = new CopyOnWriteArrayList<>();

        /**
         * 切面几何
         */
        List<SectionDTO> sectionList = new ArrayList<>();


        Polygon section = geoPolygon.toGeometry();
        //step1 将开挖多边形在投影上与三角网的关系 将所有三角网分为3个数据集  在开挖多边形内 在开挖多边形外 与多边形相交


        for (int i = 0; i < copyList.size(); i++) {

            List<Integer> single = copyList.get(i);
            //这里是处理 生成开挖多边形的点与所在三角形的三个顶点为新三角形时,清空的本来那个三角形
            if (single.size() == 0) {
                continue;
            }

            if (single.size() != 3) {
                throw new RuntimeException("三角网的点必须是三个");
            }

            /**
             * 存储三角形三个顶底的list.这里的PointWithIndex是带有原始索引的点
             */
            List<PointWithIndex> indexList = new ArrayList<>();
            for (int k = 0; k < single.size(); k++) {
                //获取索引
                Integer integer = single.get(k);
                //获取索引对应的点
                List<Double> doubles = points.get(integer);

                if (doubles.size() != 3) {
                    throw new RuntimeException("坐标必须由x,y,z 三个数构成");
                }
                double x = doubles.get(0);
                double y = doubles.get(1);
                double z = doubles.get(2);


                Coordinate coordinate = new Coordinate(x, y, z);
                PointWithIndex pointWithIndex = new PointWithIndex(coordinate, integer);
                indexList.add(pointWithIndex);

            }

            TriangleMesh triangleMesh = new TriangleMesh(indexList);
            GeoPolygon trianglePolygon = triangleMesh.getMesh();


            Polygon triangle = trianglePolygon.toGeometry();


            /*
              判断是否在开挖多边线之外
             */
            if (section.disjoint(triangle)) {
                outSide.add(single);
            } else if (section.intersects(triangle) == true && section.contains(triangle) == false) { //判断是否和开挖矩形相交

                Coordinate[] coordinates = section.getCoordinates();
                PointWithIndex point1 = triangleMesh.getPoints().get(0);
                PointWithIndex point2 = triangleMesh.getPoints().get(1);
                PointWithIndex point3 = triangleMesh.getPoints().get(2);

                int index1 = point1.getIndex();
                int index2 = point2.getIndex();
                int index3 = point3.getIndex();

                //step2 将相交三角网数据集的每个三角面和开挖范围的顶点进行判断是否点在三角形内.如果满足条件删除现有的三角形,然后根据顶点所在位置与三角形三个顶点形成新的2个或者3个三角形
                //注意这里要少循环一个点
                for (int j = 0; j < coordinates.length-1; j++) {
                    Coordinate coordinate0 = coordinates[j];
                    //如果开挖多边形顶点和三角形顶点重合则忽略,避免后续生成的新三角面再次进行了计算


                    /**
                     * 将开挖数据的顶点加入到顶点数据里面
                     */
                    GeoPoint point = new GeoPoint(coordinates[j]);


                    /**
                     * 判断点是否在三角形内或者边上
                     */

                    if (triangle.intersects(point.toGeometry())) {

                        VirtualDrillCreater virtualDrillCreater = new VirtualDrillCreater();
                        GeoPoint intersectPoint = virtualDrillCreater.getIntersectPointByMath(trianglePolygon, point);
                        if (point1.getCoordinate().equals2D(intersectPoint.getCoordinate(),0.00000001) || point2.getCoordinate().equals2D(intersectPoint.getCoordinate(),0.00000001) || point3.getCoordinate().equals2D(intersectPoint.getCoordinate(),0.00000001)) {
                            String key = createSplKey(point1.getX(),point2.getX(),point3.getX(),point1.getY(),point2.getY(),point3.getY());
                            List<List<Integer>> splIts = specialIntersects.get(key);
                            if(splIts==null){
                                splIts = new ArrayList<>();
                            }
                            splIts.add(new ArrayList<>(single));
                            specialIntersects.put(key,splIts);
                            //因为 single = copyList.get(i),下面一情况，single也跟着清空了
                            copyList.get(i).clear();
                            break;
                        }

                        points.add(Arrays.asList(intersectPoint.getX(), intersectPoint.getY(), intersectPoint.getZ()));
                        //得到这个点的索引
                        int index0 = points.size() - 1;

                        PointWithIndex point0 = new PointWithIndex(intersectPoint.getCoordinate(), index0);

                        // step3 如果点在三角形内 将三角形内的点和三角形的其中两个顶点形成新的3个三角形
                        List<Integer> list1 = new ArrayList<>();
                        List<Integer> list2 = new ArrayList<>();
                        List<Integer> list3 = new ArrayList<>();
                        /**
                         * 剔除点在直线的情况
                         */
                        if (!isInSomeline(point0, point1, point2)) {
                            // 新增内点 顶点1 顶点2
                            list1.add(index0);
                            list1.add(index1);
                            list1.add(index2);
                        }

                        if (!isInSomeline(point0, point2, point3)) {
                            // 新增内点 顶点2 顶点3
                            list2.add(index0);
                            list2.add(index2);
                            list2.add(index3);
                        }

                        if (!isInSomeline(point0, point3, point1)) {
                            // 新增内点 顶点3 顶点1
                            list3.add(index0);
                            list3.add(index3);
                            list3.add(index1);
                        }


                        //将新加的三角网索引加到列表最后,最后也会被循环到
                        copyList.add(list1);
                        copyList.add(list2);
                        copyList.add(list3);
                        copyList.get(i).clear();

                    }

                }
                //如果没有点在三角形内 ，就放入newIndexs集合准备justdoit
                if(!copyList.get(i).isEmpty()){
                    intersects.add(single);
                }
            } else if (section.contains(triangle)) {  //判断是否在开挖矩形之内
                include.add(single);
            } else {
                throw new RuntimeException("开挖的多边形" + section + "和三角网" + triangle + "关系未知");
            }

        }


        for (List<List<Integer>> value : specialIntersects.values()) {
            intersects.addAll(value);
        }

        newIndexs.addAll(intersects);
        log.info("参与生成的三角网点索引数据为"+newIndexs);
        log.info("参与生成的三角网点索引点数据"+points);


        List<List<Tangent>> tangents = new ArrayList<>();

        for (int i = 0; i < newIndexs.size(); i++) {

            List<Integer> single = newIndexs.get(i);
            if (single.isEmpty()) {
                //newIndexs.remove(0);
                continue;
            }
            TriangleMesh triangleMesh = index2Mesh(single, points);

            //获取开挖的多边形的线,逆时针排序
            List<GeoLineString> strings = getSectLineString(geoPolygon);
            //1.每条线的方向(逆时针)左边的所有点在多边形内,右边在多边形外,直线上的点根据内外点数的数量来判断属于哪一边
            log.info("step1：当初始三角网为{},多边形为{}",triangleMesh,strings);
            Result result = justDoIt(triangleMesh, strings, 0);
            List<TriangleMesh> inList  = result.getInclude();
            List<TriangleMesh> outList  = result.getOutSide();
            List<Tangent> tangent = result.getTangent();

            tangents.add(tangent);

            List<List<Integer>> indexListIn = meshList2indexList(inList);
            List<List<Integer>> indexListOut = meshList2indexList(outList);

            include.addAll(indexListIn);
            outSide.addAll(indexListOut);

        }
        //构建剖面上的交点

        if(tangents.size()%2!=0){
            throw new RuntimeException("三角形交点的数据集不为偶数");
        }

        for (int i = 0; i < tangents.size(); i++) {
            List<Tangent> tangenstUp = tangents.get(i);
            List<Tangent> tangentsDown = tangents.get(++i);

            for (int j = 0; j < tangenstUp.size(); j++) {
                Tangent up = tangenstUp.get(j);
                Tangent down = tangentsDown.get(j);


                PointWithIndex upStart = up.getPointStart();
                PointWithIndex upEnd = up.getPointEnd();

                PointWithIndex downStart = down.getPointStart();
                PointWithIndex downEnd = down.getPointEnd();

                Coordinate coordinate0 = upStart.getCoordinate();
                Coordinate coordinate1 = upEnd.getCoordinate();
                Coordinate coordinate2 = downStart.getCoordinate();
                Coordinate coordinate3 = downEnd.getCoordinate();

                if(!coordinate1.equals2D(coordinate2)){
                    Coordinate temp = coordinate2;
                    coordinate2  = coordinate3;
                    coordinate3 = temp;
                }

                //构建四边形
                GeoPolygon sectionPolygon = new GeoPolygon(new Coordinate[]{coordinate0,coordinate1,coordinate2,coordinate3});
                SectionDTO sectionDTO = new SectionDTO(sectionPolygon,level);

                sectionList.add(sectionDTO);
            }

        }

        return new ExcavatingDTO(include, outSide, points,sectionList);

    }

    /**
     * 是否为三角形的顶点
     * @param thisMesh
     * @return
     */
    private boolean isInTriangleMesh(TriangleMesh thisMesh,Dot dot){

        List<PointWithIndex> meshPoints = thisMesh.getPoints();

        for (int i = 0; i < meshPoints.size(); i++) {

            PointWithIndex pointWithIndex = meshPoints.get(i);

            double detX =  SpatialAnalysis.compute(pointWithIndex.getX(),5) - SpatialAnalysis.compute(dot.getX(),5);
            double detY =  SpatialAnalysis.compute(pointWithIndex.getY(),5) - SpatialAnalysis.compute(dot.getY(),5);
            double detZ =  SpatialAnalysis.compute(pointWithIndex.getZ(),5) - SpatialAnalysis.compute(dot.getZ(),5);

            if(detX==0&&detY==0&&detZ==0){
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @param thisMesh 一定和开挖范围多边形有交点的三角形
     * @param strings
     * @param index
     * @return
     */
    private Result justDoIt(TriangleMesh thisMesh, List<GeoLineString> strings, int index){

        List<TriangleMesh> include = new ArrayList<>();
        List<TriangleMesh> outSide = new ArrayList<>();
        List<Tangent> intersectPoints = new ArrayList<>();

        GeoLineString lineString = strings.get(index);

        Coordinate[] coordinates = lineString.getCoordinates();
        Dot dotStart = new Dot(coordinates[0].x, coordinates[0].y, coordinates[0].z);
        Dot dotEnd = new Dot(coordinates[1].x, coordinates[1].y, coordinates[1].z);

        List<PointWithIndex> meshPoints = thisMesh.getPoints();

        List<PointWithIndex> left = new ArrayList<>(), right = new ArrayList<>(), in = new ArrayList<>();
        log.info("index={}：当前三角网为{},多边形为{}",index,thisMesh,strings);
        boolean isIntersect =false;
        //如果只出现仅有顶点的交点
        boolean isOnleyVertical = false;
        for (int k = 0; k < meshPoints.size(); k++) {
            PointWithIndex pointWithIndex = meshPoints.get(k);
            Edge edge = pointWithIndex.getEdge();
            Dot intersect = getIntersect(edge, lineString, 800);

            if(intersect!=null&&!isInTriangleMesh(thisMesh,intersect)){
                isIntersect = true;
            }

            if(intersect!=null&&isInTriangleMesh(thisMesh,intersect)){
                isOnleyVertical = true;
            }
        }
        //剔除仅有交点在顶点的情况
        if(!isIntersect){
            if(isOnleyVertical){

                GeoPolygon geoPolygon = lineStrings2Polygon(strings);
                List<PointWithIndex> points = thisMesh.getPoints();
                int inner = 0;
                for (PointWithIndex pointWithIndex : points) {
                    GeoPoint point = new GeoPoint(new Coordinate(pointWithIndex.getX(), pointWithIndex.getY(), pointWithIndex.getZ()));

                    if (geoPolygon.toGeometry().contains(point.toGeometry())) {
                        inner++;
                    }

                }

                if(inner>0){
                    if (index == strings.size() - 1) {
                        include.add(thisMesh);
                        return new Result(include, outSide,intersectPoints,index);
                    }

                }else {
                    outSide.add(thisMesh);
                    return new Result(include, outSide,intersectPoints,index);
                }


            } else {
                if (index == strings.size() - 1) {
                    include.add(thisMesh);
                    return new Result(include, outSide,intersectPoints,index);
                }
            }

            Result result = justDoIt(thisMesh, strings, index + 1);
            include.addAll(result.include);
            outSide.addAll(result.outSide);
            intersectPoints.addAll(result.tangents);
        }else {

            for (int k = 0; k < meshPoints.size(); k++) {
                PointWithIndex pointWithIndex = meshPoints.get(k);
                Dot dotO = new Dot(pointWithIndex.getX(), pointWithIndex.getY(), pointWithIndex.getZ());
                double det = isLeft(dotO, dotStart, dotEnd);
                if (det > 0) {
                    left.add(pointWithIndex);
                } else if (det < 0) {

                    right.add(pointWithIndex);
                } else {
                    in.add(pointWithIndex);
                }


            }


            if (right.size() + in.size() == 3) {
                outSide.add(thisMesh);
                return new Result(include, outSide,intersectPoints,index);
            }

            if (left.size() + in.size() == 3) {

                if (index == strings.size() - 1) {
                    //结束递归
                    include.add(thisMesh);
                    return new Result(include, outSide,intersectPoints,index);
                }
                Result result = justDoIt(thisMesh, strings, index + 1);
                include.addAll(result.include);
                outSide.addAll(result.outSide);
                intersectPoints.addAll(result.tangents);
            }

            if (left.size() == right.size() && right.size() == in.size() && in.size() == 1) {
                //这种情况一定切到 in 点的对边上
                PointWithIndex pointIn = in.get(0);
                Edge edge = pointIn.getEdge();
                Dot intersect = getIntersect(edge, lineString, 800);

                points.add(Arrays.asList(intersect.getX(), intersect.getY(), intersect.getZ()));
                //得到这个点的索引
                int index0 = points.size() - 1;

                PointWithIndex point0 = new PointWithIndex(new Coordinate(intersect.getX(), intersect.getY(), intersect.getZ()), index0);
                PointWithIndex pointRight = right.get(0);
                PointWithIndex pointLeft = left.get(0);

                // TODO: 2020/11/5 是否关注一下生成新三角形的顺序
                Tangent tangent = new Tangent(point0,pointIn,index);
                intersectPoints.add(tangent);
                log.info("交点信息：[{},交点1{}由边{}和{}相交得到,交点2为顶点{},相交类型[left:1,in:1,right:1],当前三角网为:{},多边形为：{}",tangent,intersect,edge,lineString,pointIn ,thisMesh,strings);
                TriangleMesh triangleIn = new TriangleMesh(Arrays.asList(pointIn, point0, pointLeft));
                TriangleMesh triangleOut = new TriangleMesh(Arrays.asList(new PointWithIndex(pointIn.getCoordinate(),pointIn.getIndex()), new PointWithIndex(point0.getCoordinate(),point0.getIndex()), new PointWithIndex(pointRight.getCoordinate(),pointRight.getIndex())));

                outSide.add(triangleOut);
                //结束递归
                if (index == strings.size() - 1) {
                    include.add(triangleIn);
                    return new Result(include, outSide,intersectPoints,index);
                }
                Result result = justDoIt(triangleIn, strings, index + 1);
                include.addAll(result.include);
                outSide.addAll(result.outSide);
                intersectPoints.addAll(result.tangents);


            }

            if (left.size() == 1 && right.size() == 2) {

                PointWithIndex pointA = left.get(0);
                PointWithIndex pointB = right.get(0);
                PointWithIndex pointC = right.get(1);

                Edge edgeB = pointB.getEdge();
                Edge edgeC = pointC.getEdge();
                Dot intersectB = getIntersect(edgeB, lineString, 800);
                Dot intersectC = getIntersect(edgeC, lineString, 800);

                points.add(Arrays.asList(intersectB.getX(), intersectB.getY(), intersectB.getZ()));
                //得到这个点的索引
                int indexB = points.size() - 1;
                points.add(Arrays.asList(intersectC.getX(), intersectC.getY(), intersectC.getZ()));
                //得到这个点的索引
                int indexC = points.size() - 1;

                PointWithIndex pointWithIndexB = new PointWithIndex(new Coordinate(intersectB.getX(), intersectB.getY(), intersectB.getZ()), indexB);
                PointWithIndex pointWithIndexC = new PointWithIndex(new Coordinate(intersectC.getX(), intersectC.getY(), intersectC.getZ()), indexC);

                Tangent tangent = new Tangent(pointWithIndexB,pointWithIndexC,index);
                intersectPoints.add(tangent);
                log.info("交点信息：[{},交点1{}由边{}和{}相交得到,交点2{}由边{}和{}得到],相交类型[left:1,in:0,right:2],当前三角网为:{},多边形为：{}",tangent,intersectB,edgeB,lineString,intersectC,edgeC,lineString,thisMesh,strings);
                //构建三个新三角形
                TriangleMesh triangleA = new TriangleMesh(Arrays.asList(pointA, pointWithIndexC, pointWithIndexB));
                TriangleMesh triangleB = new TriangleMesh(Arrays.asList(pointB, pointC, pointWithIndexB));
                TriangleMesh triangleC = new TriangleMesh(Arrays.asList(pointB, pointWithIndexB, pointWithIndexC));

                outSide.add(triangleB);
                outSide.add(triangleC);
                //结束递归
                if (index == strings.size() - 1) {
                    include.add(triangleA);
                    return new Result(include, outSide,intersectPoints,index);
                }
                Result result = justDoIt(triangleA, strings, index + 1);
                include.addAll(result.include);
                outSide.addAll(result.outSide);
                intersectPoints.addAll(result.tangents);
            }

            if (left.size() == 2 && right.size() == 1) {

                PointWithIndex pointA = right.get(0);
                PointWithIndex pointB = left.get(0);
                PointWithIndex pointC = left.get(1);

                Edge edgeB = pointB.getEdge();
                Edge edgeC = pointC.getEdge();
                Dot intersectB = getIntersect(edgeB, lineString, 800);
                Dot intersectC = getIntersect(edgeC, lineString, 800);

                if(intersectB==null){
                    throw new NullPointerException("内有2点,外有1点情况下没有得到交点！三角形三点内点为:["+pointA+"],["+pointB+"],外点为:["+pointC+"],三角形边为+["+edgeB+"],切线为["+lineString+"]");
                }

                points.add(Arrays.asList(intersectB.getX(), intersectB.getY(), intersectB.getZ()));
                //得到这个点的索引
                int indexB = points.size() - 1;

                if(intersectC==null){
                    throw new NullPointerException("内有2点,外有1点情况下没有得到交点！三角形三点内点为:["+pointA+"],["+pointB+"],外点为:["+pointC+"],三角形边为+["+edgeC+"],切线为["+lineString+"]");
                }


                points.add(Arrays.asList(intersectC.getX(), intersectC.getY(), intersectC.getZ()));
                //得到这个点的索引
                int indexC = points.size() - 1;

                PointWithIndex pointWithIndexB = new PointWithIndex(new Coordinate(intersectB.getX(), intersectB.getY(), intersectB.getZ()), indexB);
                PointWithIndex pointWithIndexC = new PointWithIndex(new Coordinate(intersectC.getX(), intersectC.getY(), intersectC.getZ()), indexC);
                //构建三个新三角形

                Tangent tangent = new Tangent(pointWithIndexB,pointWithIndexC,index);
                intersectPoints.add(tangent);
                log.info("交点信息：[{},交点1{}由边{}和{}相交得到,交点2{}由边{}和{}得到],相交类型[left:2,in:0,right:1],当前三角网为:{},多边形为：{}",tangent,intersectB,edgeB,lineString,intersectC,edgeC,lineString,thisMesh,strings);

                TriangleMesh triangleA = new TriangleMesh(Arrays.asList(pointA, pointWithIndexC, pointWithIndexB));
                TriangleMesh triangleB = new TriangleMesh(Arrays.asList(pointB, pointC, new PointWithIndex(pointWithIndexB.getCoordinate(),pointWithIndexB.getIndex())));
                TriangleMesh triangleC = new TriangleMesh(Arrays.asList(new PointWithIndex(pointB.getCoordinate(),pointB.getIndex()), new PointWithIndex(pointWithIndexB.getCoordinate(),pointWithIndexB.getIndex()), new PointWithIndex(pointWithIndexC.getCoordinate(),pointWithIndexC.getIndex())));
                outSide.add(triangleA);

                //结束递归
                if (index == strings.size() - 1) {
                    include.add(triangleB);
                    include.add(triangleC);
                    return new Result(include, outSide,intersectPoints,index);
                }
                Result resultB = justDoIt(triangleB, strings, index + 1);
                include.addAll(resultB.include);
                outSide.addAll(resultB.outSide);
                intersectPoints.addAll(resultB.tangents);
                Result resultC = justDoIt(triangleC, strings, index + 1);
                include.addAll(resultC.include);
                outSide.addAll(resultC.outSide);
                intersectPoints.addAll(resultC.tangents);

            }

        }

        return new Result(include, outSide,intersectPoints,index);
    }

    /**
     *
     * @param tangents
     * @param size
     */
    private List<List<Tangent>>  tangentProcess(List<Tangent> tangents,int size){

        List<List<Tangent>> tangentList = new ArrayList<>();
        for (int i = 0; i <size ; i++) {
            tangentList.add(new ArrayList<>());
        }

        for (int i = 0; i < tangents.size(); i++) {
            Tangent tangent = tangents.get(i);
            int index = tangent.getLevel();

            List<Tangent> list = tangentList.get(index);
            list.add(tangent);

        }

        return tangentList;
    }

    /**
     * 从线的集合构建面
     *
     * @param lineStrings
     * @return
     */
    private GeoPolygon lineStrings2Polygon(List<GeoLineString> lineStrings) {

        List<Coordinate> coordinates = new ArrayList<>();

        for (int i = 0; i < lineStrings.size(); i++) {
            GeoLineString lineString = lineStrings.get(i);
            Coordinate coordinate = lineString.getCoordinates()[0];
            coordinates.add(coordinate);
            if (i == lineStrings.size() - 1) {
                coordinates.add(lineString.getCoordinates()[1]);
            }

        }

        return new GeoPolygon(coordinates.toArray(new Coordinate[coordinates.size()]));
    }



    private Dot getIntersect(Edge edge, GeoLineString lineString, int height) {
        GeoPolygon section = getSection(lineString, height);
        Dot start = new Dot(edge.getPointStart().getX(), edge.getPointStart().getY(), edge.getPointStart().getZ());
        Dot end = new Dot(edge.getPointEnd().getX(), edge.getPointEnd().getY(), edge.getPointEnd().getZ());
        Line line = new Line(start, end);
        Plane plane = section.toPlane();
        // 求交点
        Dot intersect = intersect(line, plane);
        Line tangent = lineString.toLine();

        if(line.containsWithXY(intersect)&&tangent.containsWithXY(intersect)){
            return intersect;
        }
        return null;

    }

    private List<Integer> toIndexList(List<PointWithIndex> indexList) {

        List<Integer> lists = new ArrayList<>();

        indexList.forEach(index -> {
            int pointindex = index.getIndex();
            lists.add(pointindex);
        });
        return lists;
    }

    /**
     * 三角网转换传输数据格式
     * @param meshes
     * @return
     */
    private List<List<Integer>> meshList2indexList(List<TriangleMesh> meshes){

        List<List<Integer>> indexList = new ArrayList<>();
        meshes.forEach(triangleMesh -> {

            List<Integer> single = new ArrayList<>();
            List<PointWithIndex> meshPoints = triangleMesh.getPoints();
            meshPoints.forEach(pointWithIndex -> single.add(pointWithIndex.getIndex()));
            indexList.add(single);
        });
        return indexList;
    }

    private TriangleMesh index2Mesh(List<Integer> single, List<List<Double>> points) {

        List<PointWithIndex> indexList = new ArrayList<>();
        for (int k = 0; k < single.size(); k++) {

            Integer integer = single.get(k);

            List<Double> doubles = points.get(integer);

            double x = doubles.get(0);
            double y = doubles.get(1);
            double z = doubles.get(2);

            Coordinate coordinate = new Coordinate(x, y, z);
            PointWithIndex pointWithIndex = new PointWithIndex(coordinate, integer);
            indexList.add(pointWithIndex);

        }

        return new TriangleMesh(indexList);

    }

    /**
     * 求直线和面的交点,注意这里的直线和面是没有范围的
     *
     * @param line
     * @param plane
     * @return
     */
    private Dot intersect(Line line, Plane plane) {

        Dot intersection = plane.getIntersection(line);

        return intersection;
    }

    /**
     * @param dotO
     * @param dotStart
     * @param dotEnd
     * @return
     */
    private boolean isLeftLine(Dot dotO, Dot dotStart, Dot dotEnd) {
        Vector vectorAB = new Vector(dotStart, dotEnd);
        Vector vectorAO = new Vector(dotStart, dotO);
        return SpatialAnalysis.det2D(vectorAB, vectorAO) > 0;
    }

    /**
     * @param dotO
     * @param dotStart
     * @param dotEnd
     * @return
     */
    private double isLeft(Dot dotO, Dot dotStart, Dot dotEnd) {
        Vector vectorAB = new Vector(dotStart, dotEnd);
        Vector vectorAO = new Vector(dotStart, dotO);
        return SpatialAnalysis.det2D(vectorAB, vectorAO);
    }

    /**
     * 判断dotO 是否在取值线的线段内
     *
     * @param dotO
     * @param dotStart
     * @param dotEnd
     * @return
     */
    private boolean inside(Dot dotO, Dot dotStart, Dot dotEnd) {
        Vector vectorAB = new Vector(dotStart, dotEnd);
        Vector vectorAO = new Vector(dotStart, dotO);

        /*
         * 还要判断点是延长线满足还是线段满足 我们这里用 向量的点乘的几何意义计算
         * https://blog.csdn.net/dcrmg/article/details/52416832
         * Vab*Vao = cosa*|Vab|*|Vao|
         * cosa = Vab*Vao/|Vab|*|Vao| 如果两边同事满足都是锐角,则条件满足
         */

        Vector vectorBA = new Vector(dotEnd, dotStart);
        Vector vectorBO = new Vector(dotEnd, dotO);


        double vOAB = vectorAB.dotProduct(vectorAO) / (vectorAB.module() * vectorAO.module());
        double vOBA = vectorBA.dotProduct(vectorBO) / (vectorBA.module() * vectorBO.module());

        return vOAB*vOBA>0;
    }

    /**
     * 判断三个点是否在同一直线上。利用经过点point1,然后就point1到point2 和point1到point3的方向向量相同证明在一条直线上
     *
     * @return
     */
    private boolean isInSomeline(PointWithIndex point1, PointWithIndex point2, PointWithIndex point3) {
        /**
         * point1到point2的方向向量
         */
        double x1 = point2.getX() - point1.getX();
        double y1 = point2.getY() - point1.getY();
        double z1 = point2.getZ() - point1.getY();
        /**
         * point1到point3的方向向量
         */
        double x2 = point3.getX() - point1.getX();
        double y2 = point3.getY() - point1.getY();
        double z2 = point3.getZ() - point1.getZ();

        //只要证明两个方向向量平行

        Vector vectorA = new Vector(x1, y1, z1);
        Vector vectorB = new Vector(x2, y2, z2);

        return vectorA.parallalTo(vectorB, 4);

    }

    private double getMinZ(GeoPolygon geoPolygon) {

        double minZ = 1000;

        Coordinate[] coordinates = geoPolygon.getCoordinates();

        for (int i = 0; i < coordinates.length; i++) {
            Coordinate coordinate = coordinates[i];
            double z = coordinate.z;
            minZ = Math.min(minZ, z);

        }
        return minZ;
    }

    /**
     * 根据前端页面绘制的开挖多边形加深度来计算对应的剖面
     *
     * @param geoPolygon
     * @param height
     * @return
     */
    private List<GeoPolygon> getSection(GeoPolygon geoPolygon, int height) {

        double minZ = getMinZ(geoPolygon);
        Coordinate[] coordinates = geoPolygon.getCoordinates();
        List<GeoPolygon> geoPolygons = new ArrayList<>();
        for (int i = 0; i < coordinates.length - 1; i++) {

            Coordinate coordinate0 = coordinates[i];
            Coordinate coordinate1 = coordinates[++i];
            //构建第三第四个顶点
            Coordinate coordinate2 = coordinate1;
            Coordinate coordinate3 = coordinate0;
            coordinate2.z = minZ - height;
            coordinate3.z = minZ - height;
            //构建
            GeoPolygon polygon = new GeoPolygon(new Coordinate[]{coordinate0, coordinate1, coordinate2, coordinate3, coordinate0});
            geoPolygons.add(polygon);

        }

        return geoPolygons;
    }

    /**
     * 根据前端页面绘制的开挖多边形的单条边来获取切面
     *
     * @param string
     * @param height
     * @return
     */
    private GeoPolygon getSection(GeoLineString string, int height) {

        Coordinate[] coordinates = string.getCoordinates();

        Coordinate coordinate0 = coordinates[0];
        Coordinate coordinate1 = coordinates[1];
        //构建第三第四个顶点
        Coordinate coordinate2 = new Coordinate(coordinate1.x, coordinate1.y, coordinate1.z - height);
        Coordinate coordinate3 = new Coordinate(coordinate0.x, coordinate0.y, coordinate0.z - height);
        ;

        GeoPolygon polygon = new GeoPolygon(new Coordinate[]{coordinate0, coordinate1, coordinate2, coordinate3, coordinate0});

        return polygon;
    }

    /**
     * 根据前端构建的开挖多边形逆时针构几何线
     *
     * @param geoPolygon
     * @return
     */
    private List<GeoLineString> getSectLineString(GeoPolygon geoPolygon) {

        List<GeoLineString> strings = new ArrayList<>();
        Coordinate[] coordinates = geoPolygon.getCoordinates();
        double max = 0;
        int j = 0;
        for (int i = 0; i < coordinates.length; i++) {

            Coordinate coordinate = coordinates[i];
            if (coordinate.x > max) {
                j = i;
                max = coordinate.x;
            }

        }


        int last = (j == 0) ? (coordinates.length - 1) : (j - 1);
        int next = (j == coordinates.length - 1) ? 0 : j + 1;

        Coordinate coordinateLast = coordinates[last];
        Coordinate coordinateThis = coordinates[j];
        Coordinate coordinateNext = coordinates[next];
        /*
         * 判断多边形是否逆时针,https://www.cnblogs.com/kyokuhuang/p/4250526.html,基本思路如果一个多边形有凸点和凹点,判断每个点和上一个点下一个点组成的向量的叉乘,如果为正的总数大于为负的总数说明为逆时针
         * 然后根据这个思路演变,最右边的点也就是X为最大的点一定是凸点,那么我只需要判断这个点的上下点所组成的向量叉乘是否为正即可
         *
         */
        Vector vectorLast = new Vector(new Dot(coordinateThis.x, coordinateThis.y, coordinateThis.z), new Dot(coordinateLast.x, coordinateLast.y, coordinateLast.z));
        Vector vectorNext = new Vector(new Dot(coordinateThis.x, coordinateThis.y, coordinateThis.z), new Dot(coordinateNext.x, coordinateNext.y, coordinateNext.z));

        double det2D = SpatialAnalysis.det2D(vectorNext, vectorLast);


        for (int i = 0; i < coordinates.length - 1; i++) {
            int indexThis = i;
            int indexNext = i + 1;
            if (det2D < 0) {
                indexThis = coordinates.length - 1 - indexThis;
                indexNext = coordinates.length - 1 - indexNext;
            }

            Coordinate coordinateA = coordinates[indexThis];
            Coordinate coordinateB = coordinates[indexNext];
            GeoLineString string = new GeoLineString(coordinateA, coordinateB);
            strings.add(string);

        }

        return strings;
    }

    class Result {

        private List<TriangleMesh> include;
        private List<TriangleMesh> outSide;

        private List<Tangent> tangents;

        private int index;

        Result(List<TriangleMesh> include, List<TriangleMesh> outSide,List<Tangent> tangents, int index) {
            this.include = include;
            this.outSide = outSide;
            this.index = index;
            this.tangents = tangents;
        }



        public List<Tangent> getTangent() {
            return tangents;
        }

        public int getIndex() {
            return index;
        }

        public List<TriangleMesh> getInclude() {
            return include;
        }

        public List<TriangleMesh> getOutSide() {
            return outSide;
        }

        @Override
        public String toString() {
            return "Result{" +
                    "include=" + include +
                    ", outSide=" + outSide +
                    ", index=" + index +
                    '}';
        }
    }
}
